// Copyright (C) Calum Grant 2008

#include <windows.h>

namespace dynamic
{
	namespace api
	{
		/// A mutual exclusion.
		/** Recursive */
		class mutex : cg::not_copyable, cg::not_assignable
		{
		public:
			mutex()
			{
				InitializeCriticalSection(&m_cs);
			}

			~mutex()
			{
				DeleteCriticalSection(&m_cs);
			}

			void lock()
			{
				EnterCriticalSection(&m_cs);
			}

			void unlock()
			{
				LeaveCriticalSection(&m_cs);
			}

		private:
			CRITICAL_SECTION m_cs;
			mutex(const mutex&);
		};


		/// A thread of execution.
		/** The purpose of this class is to abstract away the operation system. */
		class thread : cg::not_copyable, cg::not_assignable
		{
			HANDLE h_thread;
			typedef void thread_func(void*);
			thread_func *m_fn;
			void * m_data;

			static DWORD WINAPI thread_proc(LPVOID param)
			{
				thread * self = static_cast<thread*>(param);
				self->m_fn(self->m_data);
				return 0;
			};

		public:
			thread() : h_thread(INVALID_HANDLE_VALUE) 
			{ 
			}

			void run(thread_func start, LPVOID data)
			{
				m_fn = start;
				m_data = data;
				h_thread = ::CreateThread(0, 0, &thread_proc, this, 0, 0);
			}

			~thread() 
			{ 
				if(h_thread != INVALID_HANDLE_VALUE) CloseHandle(h_thread); 
			}

			void join()
			{
				::WaitForSingleObject(h_thread, INFINITE);
			}
		};


		/// A synchronization primitive.
		class event : cg::not_copyable, cg::not_assignable
		{
			HANDLE m_handle;

		public:
			event()
			{
				m_handle = ::CreateEvent(0, 0, 0, 0);
			}

			~event()
			{
				if(m_handle != INVALID_HANDLE_VALUE) ::CloseHandle(m_handle);
			}

			void wait()
			{
				::WaitForSingleObject(m_handle, INFINITE);
			}

			void signal()
			{
				::SetEvent(m_handle);
			}
		};


		/// Pauses execution for a number of seconds.
		inline void sleep(int s)
		{
			::Sleep(s * 1000);
		}


#if defined(__CYGWIN__)
		typedef long int interlocked_var;
#else
		typedef LONG volatile interlocked_var;
#endif

		/// An atomic (threadsafe) integer which supports ++ and --.
		/** \ingroup  mem */
		class atomic_counter
		{
		public:
			atomic_counter() : m_counter(0) 
			{ 
			}

			LONG operator++() 
			{ 
				return InterlockedIncrement(&m_counter); 
			}

			LONG operator++(int) 
			{ 
				return InterlockedIncrement(&m_counter)-1; 
			}

			LONG operator--() 
			{ 
				return InterlockedDecrement(&m_counter); 
			}

			LONG operator--(int) 
			{ 
				return InterlockedDecrement(&m_counter)+1; 
			}

		private:
			interlocked_var m_counter;
		};


		/// Maintain a count of a stack.
		/** This is used by message queues to maintain the size of the queue,
			or wait for objects to be added.  The strange thing is that
			the implementation is completely different in Win32 and pthreads.
			*/
		class stack : cg::not_copyable, cg::not_assignable
		{
			int m_size;
			bool m_shutdown;
			CRITICAL_SECTION m_cs;
			HANDLE m_item_available, m_empty;

		public:
			/// Initialise an empty stack.
			stack() : 
				m_size(0),
				m_shutdown(false),
				m_item_available(::CreateEvent(0, 1, 0, 0)),
				m_empty(::CreateEvent(0, 1, 1, 0))
			{ 
				::InitializeCriticalSection(&m_cs);
			}

			~stack()
			{
				::DeleteCriticalSection(&m_cs);
				::CloseHandle(m_empty);
				::CloseHandle(m_item_available);
			}

			/// Sets the state to shutting down and awakens all waiting threads.
			void shutdown()
			{
				EnterCriticalSection(&m_cs);
				m_shutdown = true;
				::SetEvent(m_item_available);
				LeaveCriticalSection(&m_cs);
			}

			/// Returns true if we are shutting down.
			bool check_shutdown()
			{
				EnterCriticalSection(&m_cs);
				bool ret = m_shutdown;
				LeaveCriticalSection(&m_cs);
				return ret;
			}

			/// Adds an item.
			void push()
			{
				EnterCriticalSection(&m_cs);
				++m_size;
				::SetEvent(m_item_available);
				::ResetEvent(m_empty);
				LeaveCriticalSection(&m_cs);				
			}

			/// Removes an item from the stack, or waits until an item is added.
			/** Returns false if we are shutting down */
			bool wait_pop()
			{
				EnterCriticalSection(&m_cs);
				while(m_size==0 && !m_shutdown)
				{
					LeaveCriticalSection(&m_cs);
					WaitForSingleObject(m_item_available, INFINITE);
					EnterCriticalSection(&m_cs);
				}
				if(m_shutdown) 
				{
					LeaveCriticalSection(&m_cs);				
					return false;
				}
				--m_size;
				if(m_size==0) 
				{
					::ResetEvent(m_item_available);
					::SetEvent(m_empty);
				}
				LeaveCriticalSection(&m_cs);
				return true;
			}

			/// Waits until the stack is empty.
			void wait_empty()
			{
				WaitForSingleObject(m_empty, INFINITE);
			}
		};
	}
}
